﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ParallelRuntimeLibrary.Internal
{
    /// <summary>
    /// Concurrent Enabeld (Free-Lock) tasks queue.
    /// </summary>
    internal sealed class TaskQueue
    {
        private sealed class TQElement
        {
            internal Task Value;
            internal TQElement Next;

            internal TQElement(Task value)
            {
                Value = value; Next = null;
            }
        }

        private TQElement Head, Tail;

        /*///////////////////////////////////////////////////////////////////////////////////////////////*/

        #region Class Constructor

        public TaskQueue()
        {
            Head = new TQElement(null);
            Tail = Head;
        }

        #endregion

        /*///////////////////////////////////////////////////////////////////////////////////////////////*/

        #region Task Queue Operations

        public void Enqueue(Task value)
        {
            TQElement tmp = new TQElement(value);
            TQElement oldLast = null;

            do
	        {
                oldLast = Tail;
                tmp.Next = oldLast.Next;

                if ((tmp.Next == null) && Interlocked.CompareExchange(ref Tail.Next, tmp, null) == null)
                {
                    Interlocked.CompareExchange<TaskQueue.TQElement>(ref Tail, tmp, oldLast);
                    return;
                }
                else
                {
                    TQElement oldLast2 = Tail;
                    for (var node2 = oldLast2.Next; node2 != null; node2 = oldLast2.Next)
                    {
                        if (Interlocked.CompareExchange<TaskQueue.TQElement>(ref this.Tail, node2, oldLast2) != oldLast2)
                            NativeMethod.SwitchToThread();

                        oldLast2 = Tail;
                    }
                }
            } while (true);
        }

        public Task Dequeue()
        {
            Task tmp = null;
            TQElement oldFirst;
            TQElement oldFirstNext;

            do
            {
                oldFirst = Head;
                oldFirstNext = oldFirst.Next;

                if (oldFirstNext != null && oldFirst == Interlocked.CompareExchange(ref Head, oldFirstNext, oldFirst))
                {
                    tmp = oldFirstNext.Value;
                    return tmp;
                }
                else
                {
                    oldFirst = Head;
                    oldFirstNext = oldFirst.Next;

                    if (oldFirstNext != null && oldFirst == Interlocked.CompareExchange(ref Head, oldFirstNext, oldFirst))
                    {
                        tmp = oldFirstNext.Value;
                        return tmp;
                    }
                    else if (oldFirstNext == null)
                        return tmp;
                    else
                        NativeMethod.SwitchToThread();
                }
            } while (true);
        }
                      
        public int Count
        {
            get
            {
                int r = 0;

                TQElement tmp = Head.Next;

                while (tmp != null)
                {
                    if (tmp.Value != null) ++r;
                    Interlocked.Exchange(ref tmp, tmp.Next);
                }

                return r;
            }
        }

        public void Remove(string id, bool startWith)
        {
            TQElement tmp = Head.Next;

            while (tmp != null)
            {
                if (startWith)
                {
                    if (tmp.Value != null && tmp.Value.Identity.StartsWith(id))
                        tmp.Value = null;
                }
                else
                {
                    if (tmp.Value != null && tmp.Value.Identity == id)
                        tmp.Value = null;
                }
                Interlocked.Exchange(ref tmp, tmp.Next);
            }

        }

        #endregion

    }
}
